package owg.util.editor2d;

import java.util.ArrayList;
import java.util.HashMap;

import owg.util.Calc;
import owg.util.awt.BooleanEditor;
import owg.util.awt.EnumEditor;
import owg.util.awt.StringArrayEditor;
import owg.util.awt.StringConstantEditor;
import owg.util.awt.StringFormattedEditor;
import owg.util.awt.StringListEditor;
import owg.util.awt.TypeEditor;
import owg.util.editor2d.entity.SP;
import owg.util.editor2d.entity.SP.StringProcessor;
import owg.util.euclidian.V2D;
import owg.util.euclidian.V2F;
import owg.util.euclidian.V3D;
import owg.util.euclidian.V3F;
import owg.util.opengl.ColorF;

public class TypeEditorFactory {
    public interface TypeEditorGenerator<X>
    {
	public TypeEditor<X> getEditor(Class<X> type, X value, SP modifiers);
    }

    private static final HashMap<Class<?>, TypeEditorGenerator<?>> generators;
    static 
    {
	generators = new HashMap<>();
	@SuppressWarnings("rawtypes")
	TypeEditorGenerator<Enum<?>> enumGen = new TypeEditorGenerator<Enum<?>>() {
	    @SuppressWarnings("unchecked")
	    @Override
	    public TypeEditor<Enum<?>> getEditor(Class<Enum<?>> type, Enum<?> value, SP modifiers) {
		return new EnumEditor(type, (value==null&&!modifiers.contains(SP.AllowNull.class))?type.getEnumConstants()[0]:value);
	    }
	};
	registerEditor(Enum.class, enumGen);
	TypeEditorGenerator<ArrayList<String>> listGen = new TypeEditorGenerator<ArrayList<String>>() {

	    @Override
	    public TypeEditor<ArrayList<String>> getEditor(
		    Class<ArrayList<String>> type, ArrayList<String> value,
		    SP modifiers) {
		return new StringListEditor(value, modifiers.getFilter(), modifiers.getSplitDelimiter(), 
			modifiers.getDisplayDelimiter(), modifiers.shouldRetainEmptyStrings());
	    }

	};
	registerEditor(ArrayList.class, listGen);
	registerEditor(String[].class, (Class<String[]> type, String[] value, SP modifiers) -> 
	{ return new StringArrayEditor(value, modifiers.getFilter(), modifiers.getSplitDelimiter(), 
		modifiers.getDisplayDelimiter(), modifiers.shouldRetainEmptyStrings()); });

	registerEditor(V2D.class, (Class<V2D> type, V2D value, SP modifiers) -> { return new StringFormattedEditor<V2D>(type, value==null?new V2D():value); });
	registerEditor(V2F.class, (Class<V2F> type, V2F value, SP modifiers) -> { return new StringFormattedEditor<V2F>(type, value==null?new V2F():value); });
	registerEditor(V3D.class, (Class<V3D> type, V3D value, SP modifiers) -> { return new StringFormattedEditor<V3D>(type, value==null?new V3D():value); });
	registerEditor(V3F.class, (Class<V3F> type, V3F value, SP modifiers) -> { return new StringFormattedEditor<V3F>(type, value==null?new V3F():value); });
	
	registerEditor(ColorF.class, (Class<ColorF> type, ColorF value, SP modifiers) -> 
		{ return new StringFormattedEditor<ColorF>(type, value==null?new ColorF(1,1,1,1):value); });

	registerEditor(Byte.class, (Class<Byte> type, Byte value, SP modifiers) -> 
		{ return new StringFormattedEditor<Byte>(type, value==null?0:value); });
	registerEditor(Short.class, (Class<Short> type, Short value, SP modifiers) -> 
		{ return new StringFormattedEditor<Short>(type, value==null?0:value); });
	registerEditor(Integer.class, (Class<Integer> type, Integer value, SP modifiers) -> 
		{ return new StringFormattedEditor<Integer>(type, value==null?0:value); });
	registerEditor(Long.class, (Class<Long> type, Long value, SP modifiers) -> 
		{ return new StringFormattedEditor<Long>(type, value==null?0:value); });
	registerEditor(Float.class, (Class<Float> type, Float value, SP modifiers) -> 
		{ return new StringFormattedEditor<Float>(type, value==null?0:value); });
	registerEditor(Double.class, (Class<Double> type, Double value, SP modifiers) -> 
		{ return new StringFormattedEditor<Double>(type, value==null?0:value); });
	registerEditor(Character.class, (Class<Character> type, Character value, SP modifiers) -> 
		{ return new StringFormattedEditor<Character>(type, value==null?'a':value); });


	registerEditor(Boolean.class, (Class<Boolean> type, Boolean value, SP modifiers) -> { return new BooleanEditor(value==null?false:value); });

	registerEditor(String.class, (Class<String> type, String value, SP modifiers) -> 
	{ 
	    if(modifiers.contains(SP.FileList.class))
		return new StringConstantEditor(value, modify(Calc.filter(
			Calc.listFileNames(modifiers.getFolder(), 
				modifiers.getFileTypes().allowFiles(), modifiers.getFileTypes().allowFolders()), 
				modifiers.getFilter()), modifiers.getStringProcessor()), modifiers.contains(SP.AllowNull.class));
	    else
		return new StringFormattedEditor<String>(type, value==null?"":value);
	});
    }

    public static<Y> void registerEditor(Class<Y> type, TypeEditorGenerator<? extends Y> gen)
    {
	generators.put(type, gen);
    }

    private static String[] modify(String[] fnames, Class<? extends StringProcessor> processorClass) {
	StringProcessor p;
	try {
	    p = processorClass.newInstance();
	    for(int i = 0; i<fnames.length; i++)
	    {
		fnames[i] = p.modify(fnames[i]);
	    }
	} catch (Exception e) {
	    throw new RuntimeException(e);
	}
	return fnames;
    }

    public static<Y> TypeEditor<Y> getEditor(Class<Y> type, Y value, SP modifiers)
    {
	if(type.isPrimitive())
	    type = Boxed.getBoxedType(type);
	@SuppressWarnings("unchecked")
	TypeEditorGenerator<Y> gen = (TypeEditorGenerator<Y>) generators.get(type);
	if(gen == null)
	    throw new IllegalArgumentException("No editor defined for argument type: "+type);
	return gen.getEditor(type, value, modifiers);
    }
}
